#include "datacontroller.h"
#include "fuzzyxmlparser.h"
#include "fuzzyxmlwriter.h"

/* I'm not 100% sure about why these declarations need to be here.  My first
    guess is because the public functions, being declared before the static
    variables leads the compiler to get confused about what elements we're
    talking about when we reference them in the instance getter.
 */
DataController* DataController::pInstance = 0;
QMutex DataController::threadLock;

DataController::DataController()
{
}

DataController* DataController::instance()
{
    if ( pInstance == 0 )
    {
        // Acquire thread lock
        threadLock.lock();
        if ( pInstance == 0 )
        {
            pInstance = new DataController;
        }
        threadLock.unlock();
    }
    return pInstance;
}

void DataController::setInputOneFuzzyValues(const double& newValue)
{
    inputOne.setFuzzyValues(newValue);
}

void DataController::setInputTwoFuzzyValues(const double& newValue)
{
    inputTwo.setFuzzyValues(newValue);
}

/* Similar function to setPoint but for the fuzzy values.*/
void DataController::setFuzzyValue(const Collection* collection,
                                   const double& newValue)
{
    if(collection == &inputOne) inputOne.setFuzzyValues(newValue);
    else if(collection == &inputTwo) inputTwo.setFuzzyValues(newValue);
}

void DataController::setOutputFuzzyValues(const double &newValue)
{
    output.setFuzzyValues(newValue);
}

double DataController::getInputOneInputValue()  const
{
    return inputOne.getInputValue();
}

double DataController::getInputTwoInputValue()  const
{
    return inputTwo.getInputValue();
}

double DataController::getOutputCrispNumber()
{
    return output.crispNumber();
}

int DataController::getRuleListSize() const
{
    return ruleList.size();
}

const QStringList& DataController::getInputOneNames() const
{
    return inputOneNames;
}

const QStringList& DataController::getInputTwoNames() const
{
    return inputTwoNames;
}

const QStringList& DataController::getOutputNames() const
{
    return outputNames;
}

void DataController::loadDataFromFile(const QString& fileName)
{
    /* Create an XML parser and load the three fuzzy collections from it, as
        well as a rule list.
     */
    FuzzyXmlParser parser(fileName);
    parser.loadCollectionFile(&inputOne, false);
    parser.loadCollectionFile(&inputTwo, false);
    parser.loadCollectionFile(&output, true);
    parser.loadRuleFile(&ruleList);

    /* Now that the rule list has been loaded, lists of all available input
        sets and output sets should be generated for faster lookup later on.
     */
    initializeSetNameLists();

    initializeFuzzyInputNumbers();
}

void DataController::saveDataToFile(const QString& filename) const
{
    /* Create an XML writer and write the three fuzzy collection to it, as well
        as well as the current rule list.
     */
    FuzzyXmlWriter writer(filename);
    writer.beginScenario();
    writer.writeCollection(inputOne);
    writer.writeCollection(inputTwo);
    writer.writeCollection(output);
    writer.writeRules(ruleList);
    writer.closeScenario();
}

void DataController::setPoint(const Collection *collection, const int &set,
                              const int &point, const double &newPos)
{
    /* Find the collection that the user intends to change a point for, then
        change the point accordingly.
     */
    if( collection == &inputOne ) inputOne.setPoint(set, point, newPos);
    else if( collection == &inputTwo ) inputTwo.setPoint(set, point, newPos);
    else if( collection == &output ) output.setPoint(set, point, newPos);
}

void DataController::setRuleList(const QVector<Rule *>& newRuleList)
{
    /* Delete the old rule list, because it is no longer needed */
    while ( !ruleList.isEmpty() )
    {
        delete ruleList.back();
        ruleList.pop_back();
    }

    ruleList = newRuleList;
}
const QString& DataController::findRule(const QString &inputOneName,
                              const QString &inputTwoName)
{
    for (int i = 0; i< this->ruleList.size(); ++i)
    {
        if (ruleList.at(i)->getFirstInput() == inputOneName
            && ruleList.at(i)->getSecondInput() == inputTwoName)
            return ruleList.at(i)->getOutput();
    }
    qDebug() << "rule not found.";
    return outputNames.at(outputNames.size()-1);

}

void DataController::initializeSetNameLists()
{
    /* Clear all data from the lists that may already exist */
    inputOneNames.clear();
    inputTwoNames.clear();
    outputNames.clear();

    /* Iterate through all of the sets and collect all unique names into the
        name lists.
     */
//    for ( int i = 0; i < ruleList.size(); ++i )
//    {
//        /* Get references to the three strings so that they can be accessed
//            without excessive function calls
//         */
//        const QString& oneName = ruleList[i]->getFirstInput();
//        const QString& twoName = ruleList[i]->getSecondInput();
//        const QString& outName = ruleList[i]->getOutput();
//
//        if ( !inputOneNames.contains(oneName))
//            inputOneNames.push_back(oneName);
//        if ( !inputTwoNames.contains(twoName))
//            inputTwoNames.push_back(twoName);
//        if ( !outputNames.contains(outName))
//            outputNames.push_back(outName);
//    }

    for ( int i = 0; i < inputOne.getSize(); ++i)
    {
        const QString& name = inputOne.getSet(i)->getName();
        if( !inputOneNames.contains(name))
            inputOneNames.push_back(name);
    }
    for ( int i = 0; i < inputTwo.getSize(); ++i)
    {
        const QString& name = inputTwo.getSet(i)->getName();
        if( !inputTwoNames.contains(name))
            inputTwoNames.push_back(name);
    }
    for ( int i = 0; i < output.getSize(); ++i)
    {
        const QString& name = output.getSet(i)->getName();
        if( !outputNames.contains(name))
            outputNames.push_back(name);
    }
     outputNames.push_back("NO RULE");
}

void DataController::initializeFuzzyInputNumbers()
{
    inputOne.setRandomFuzzyValue();
    inputTwo.setRandomFuzzyValue();
    output.crispNumber();

}

//void DataController::updateSetNameLists()
//{
//    /*Do not clear the name lists. Instead simply check for new names.
//     * This is stupid. I should just make the other one a public function,
//     * why is it private anyhow? I could encapsulate it, but it is a pretty
//     * safe function if you ask me.
//     * actually, the namelists should be set up at the MF function level...
//     */
//    for ( int i = 0; i < inputOne.getSize(); ++i)
//    {
//        const QString& name = inputOne.getSet(i)->getName();
//        if( !inputOneNames.contains(name))
//            inputOneNames.push_back(name);
//    }
//    for ( int i = 0; i < inputTwo.getSize(); ++i)
//    {
//        const QString& name = inputTwo.getSet(i)->getName();
//        if( !inputTwoNames.contains(name))
//            inputTwoNames.push_back(name);
//    }
//    for ( int i = 0; i < output.getSize(); ++i)
//    {
//        const QString& name = output.getSet(i)->getName();
//        if( !outputNames.contains(name))
//            outputNames.push_back(name);
//    }
//    if( !outputNames.contains("NO RULE"))
//        outputNames.push_back("NO RULE");
//
//}











